--[[
Copyright 2017 YANG Huan (sy.yanghuan@gmail.com).

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--]]

-- Compiled from https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/Random.cs
-- Generated by CSharp.lua Compiler
-- Licensed to the .NET Foundation under one or more agreements.
-- The .NET Foundation licenses this file to you under the MIT license.
-- See the LICENSE file in the project root for more information.
local System = System
local ArrayInt32 = System.Array(System.Int32)
System.define("System.Random", (function ()
  local Sample, InternalSample, GenerateSeed, Next, GetSampleForLargeRange, Next1, Next2, NextDouble, 
  NextBytes, internal, __ctor__, rnd
  internal = function (this)
    this._seedArray = ArrayInt32:new(56)
  end
  __ctor__ = function (this, Seed)
    if not Seed then Seed = GenerateSeed() end
    internal(this)
    local ii = 0
    local mj, mk

    --Initialize our Seed array.
    local subtraction = (Seed == -2147483648 --[[Int32.MinValue]]) and 2147483647 --[[Int32.MaxValue]] or math.Abs(Seed)
    mj = 161803398 --[[Random.MSEED]] - subtraction
    this._seedArray:set(55, mj)
    mk = 1
    for i = 1, 54 do
      --Apparently the range [1..55] is special (Knuth) and so we're wasting the 0'th position.
      ii = ii + 21
      if ii >= 55 then
        ii = ii - 55
      end
      this._seedArray:set(ii, mk)
      mk = mj - mk
      if mk < 0 then
        mk = mk + 2147483647 --[[Random.MBIG]]
      end
      mj = this._seedArray:get(ii)
    end
    for k = 1, 4 do
      for i = 1, 55 do
        local n = i + 30
        if n >= 55 then
          n = n - 55
        end
        this._seedArray:set(i, this._seedArray:get(i) - this._seedArray:get(1 + n))
        if this._seedArray:get(i) < 0 then
          this._seedArray:set(i, this._seedArray:get(i) + 2147483647 --[[Random.MBIG]])
        end
      end
    end
    this._inext = 0
    this._inextp = 21
    Seed = 1
  end
  Sample = function (this)
    --Including this division at the end gives us significantly improved
    --random number distribution.
    return (InternalSample(this) * (4.6566128752458E-10 --[[1.0 / MBIG]]))
  end
  InternalSample = function (this)
    local retVal
    local locINext = this._inext
    local locINextp = this._inextp

    locINext = locINext + 1
    if locINext >= 56 then
      locINext = 1
    end
    locINextp = locINextp + 1
    if locINextp >= 56 then
      locINextp = 1
    end

    retVal = this._seedArray:get(locINext) - this._seedArray:get(locINextp)

    if retVal == 2147483647 --[[Random.MBIG]] then
      retVal = retVal - 1
    end
    if retVal < 0 then
      retVal = retVal + 2147483647 --[[Random.MBIG]]
    end

    this._seedArray:set(locINext, retVal)

    this._inext = locINext
    this._inextp = locINextp

    return retVal
  end
  GenerateSeed = function ()
    if not rnd then
      math.randomseed(os.time())
      rnd = math.random
    end
    return rnd(0, 2147483647)
  end
  Next = function (this)
    return InternalSample(this)
  end
  GetSampleForLargeRange = function (this)
    -- The distribution of double value returned by Sample 
    -- is not distributed well enough for a large range.
    -- If we use Sample for a range [int.MinValue..int.MaxValue)
    -- We will end up getting even numbers only.

    local result = InternalSample(this)
    -- Note we can't use addition here. The distribution will be bad if we do that.
    local negative = (InternalSample(this) % 2 == 0) and true or false
    -- decide the sign based on second sample
    if negative then
      result = - result
    end
    local d = result
    d = d + (2147483646 --[[int.MaxValue - 1]])
    -- get a number in range [0 .. 2 * Int32MaxValue - 1)
    d = d / (4294967293 --[[2 * (uint)int.MaxValue - 1]])
    return d
  end
  Next1 = function (this, minValue, maxValue)
    if minValue > maxValue then
      System.throw(System.ArgumentOutOfRangeException("minValue" --[[nameof(minValue)]], "'minValue' cannot be greater than maxValue."))
    end

    local range = maxValue - minValue
    if range <= 2147483647 --[[Int32.MaxValue]] then
      return (System.ToInt32((Sample(this) * range)) + minValue)
    else
      return System.toInt32((System.ToInt64((GetSampleForLargeRange(this) * range)) + minValue))
    end
  end
  Next2 = function (this, maxValue)
    if maxValue < 0 then
      System.throw(System.ArgumentOutOfRangeException("maxValue" --[[nameof(maxValue)]], "'maxValue' must be greater than zero."))
    end
    return System.ToInt32((Sample(this) * maxValue))
  end
  NextDouble = function (this)
    return Sample(this)
  end
  NextBytes = function (this, buffer)
    if buffer == nil then
      System.throw(System.ArgumentNullException("buffer" --[[nameof(buffer)]]))
    end
    do
      local i = 0
      while i < #buffer do
        buffer:set(i, System.toByte(InternalSample(this)))
        i = i + 1
      end
    end
  end
  return {
    _inext = 0,
    _inextp = 0,
    Sample = Sample,
    Next = Next,
    Next1 = Next1,
    Next2 = Next2,
    NextDouble = NextDouble,
    NextBytes = NextBytes,
    __ctor__ = __ctor__
  }
end)())
